#pragma once
#include "PDTNode.h"
#include "PDTNumConst.h"
#include <map>

class PDTType: public PDTNode, public TraverseIfc <PDTType, PDTNode>  { 
    public: TRAVERSAL_IFC_IMPL(PDTType)
public:
    PDTType() {
    }

	PDTType(PDTCodeGraph & CG) ;
	virtual bool isType() {
		return true;
	}

	virtual NodeTypeEnum getType();

    virtual stringstream& toString(stringstream& ss) const ;
    static PDTType Int;
    static PDTType Bool;
    static PDTType Void;
};

class PDTSet :  public PDTType, public TraverseIfc <PDTSet, PDTNode>  { public:  TRAVERSAL_IFC_IMPL(PDTSet)
public:
	string name;
	PDTSet(PDTCodeGraph & CG, string aName, PDTType * eType = NULL) ;

	PDTType * getElementType() ; 

	virtual bool isSet() {
		return true;
	}

    virtual string & getName() {
        return name;
    }

	NodeTypeEnum getType() {
		return PDTSet_Em;
	}

	virtual stringstream& toString(stringstream& ss) const ;
};

class PDTIntRange  :  public PDTSet, public TraverseIfc <PDTIntRange , PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTIntRange )
    private:
	PDTExp * start;
	PDTExp * end;
    public:
        PDTIntRange(PDTCodeGraph & CG, string aName, PDTExp * aStart, PDTExp * aEnd) ;

        NodeTypeEnum getType() {
            return PDTIntRange_Em;
        }

        PDTExp * getStart()
        {
        	return start;
        }

        PDTExp * getEnd()
        {
        	return end;
        }

        virtual stringstream& toString(stringstream& ss) const ;
};

class PDTSequence :  public PDTSet, public TraverseIfc <PDTSequence, PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTSequence)
private:
    PDTIntRange * range;
    PDTIntRange * elRange;
    unsigned int size;
public:
    typedef map<unsigned int, PDTNode * > ElementsMap_t;
    ElementsMap_t elements; //A map containing the elements of the sequence
	PDTSequence(PDTCodeGraph & CG, string aName, PDTType * eType = NULL, PDTIntRange * aRange = NULL, PDTIntRange * aelRange = NULL, unsigned int aSize = 0) ;

	virtual bool isSequence() {
		return true;
	}
	NodeTypeEnum getType() {
		return PDTSequence_Em;
	}
	bool isElementInt() {
		return (getElementType() == &PDTType::Int);
	}
	bool isElementBoolean() {
		return (getElementType() == &PDTType::Bool);
	}

    PDTIntRange * getRange() const {
        return range;
    }

    PDTIntRange * getElRange() const {
        return elRange;
    }

	//Get the element inside the sequence at the index idx
	PDTNode * getElement(unsigned int idx)
	{
		ElementsMap_t::iterator it = elements.find(idx);
		if (it != elements.end()) //Not Null
		{
			return it->second;
		}
		return NULL;
	}

    unsigned int getSize() const;

	int getSeqSize()
	{
		return elements.size();
	}

	virtual stringstream& toString(stringstream& ss) const ;
};
typedef PDTSequence PDTArray;

class PDTPair :  public PDTType, public TraverseIfc <PDTPair, PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTPair)
public:
	PDTPair(PDTCodeGraph & CG, PDTType & aFrom, PDTType & aTo) :
		PDTType(CG) {
		addChild(&aFrom);
		aFrom.addParent(this);

		addChild(&aTo);
		aTo.addParent(this);

	}
	PDTType * getFirst() const {
		return dynamic_cast<PDTType*> (children[0]);
	}
	PDTType * getSecond() const {
		return dynamic_cast<PDTType*> (children[1]);
	}

	virtual bool isPair() {
		return true;
	}

	NodeTypeEnum getType() {
		return PDTPair_Em;
	}
	virtual stringstream& toString(stringstream& ss) const {
		ss << "[";
		getFirst()->toString(ss);
		ss << " , ";
		getSecond()->toString(ss);
		ss << "]";
		return ss;
	}

};

class PDTRelation :  public PDTSet, public TraverseIfc <PDTRelation, PDTNode> { public:  TRAVERSAL_IFC_IMPL(PDTRelation)
public:
	PDTRelation(PDTCodeGraph & CG, string name, PDTType & aFrom, PDTType & aTo) :
		PDTSet(CG, name, new PDTPair(CG, aFrom, aTo)) {
	}

	PDTPair * getFromToPair() const {
		return dynamic_cast<PDTPair*> (children[0]);
	}

	PDTType * getFrom() const {
		return getFromToPair()->getFirst();
	}
	PDTType * getTo() const {
		return getFromToPair()->getSecond();
	}

	NodeTypeEnum getType() {
		return PDTRelation_Em;
	}
	virtual bool isRelation() {
		return true;
	}

	~PDTRelation() { /*delete elemenType;*/
	}//this is  the pair we allocated!
	virtual stringstream& toString(stringstream& ss) const {
		ss << "Relation( ";
		getFrom()->toString(ss);
		ss << " to ";
		getTo()->toString(ss);
		ss << ") ";
		return ss;
	}
};


